package com.denghq.projectbuilder.component.web.config.handle;

import com.denghq.projectbuilder.common.CodeMsg;
import com.denghq.projectbuilder.common.ResultInfo;
import com.denghq.projectbuilder.common.exception.BussinessException;
import com.denghq.projectbuilder.common.exception.CloudException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.sql.SQLException;
import java.sql.SQLTimeoutException;
import java.util.List;
import java.util.Set;

/*
 *
 * 全局异常处理类
 *
 * @author denghq
 * @date 2018/10/29
 */
@ControllerAdvice
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {


    /**
     * 全局异常处理
     *
     * @param request
     * @param exception
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public Object allExceptionHandler(HttpServletRequest request, Exception exception) {
        //
        //
/**
 * ,DB_NETWORK_EXCPTION(50001, "数据库连接异常"), DB_TIMEOUT(50002, "数据库响应超时"), DB_PRIMARKKEY_CONFLICT(50003, "数据库违反唯一约束"),
 *     DB_CONSTRAINT_ERROR(50004, "数据违反数据库检查约束"), DB_OTHER_ERROR(51000, "其他未知数据库相关问题")
 */
        //json结果处理
        //异常处理顺序 ：1.业务异常(参数验证异常，业务异常等)
        //               2.重复插入异常
        //               3.404 异常
        //               4.权限异常
        // 				 5.500异常
        if (exception instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException e = (MethodArgumentNotValidException) exception;
            List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
            StringBuilder msg = new StringBuilder();
            int i = 1;
            int size = allErrors.size();
            for (ObjectError item : allErrors) {
                msg.append(item.getDefaultMessage());
                if (i < size) {
                    i++;
                    msg.append("|");
                }
            }
            return ResultInfo.error(CodeMsg.PARAMETER_ERROR, msg.toString());
        } else if (exception instanceof ValidationException) {
            StringBuilder msg = new StringBuilder();
            if (exception instanceof ConstraintViolationException) {
                ConstraintViolationException exs = (ConstraintViolationException) exception;
                Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
                int i = 1;
                int size = violations.size();
                for (ConstraintViolation<?> item : violations) {
                    msg.append(item.getMessage());
                    if (i < size) {
                        i++;
                        msg.append("|");
                    }
                }
            } else {
                msg.append(exception.getMessage());
            }
            return ResultInfo.error(CodeMsg.PARAMETER_ERROR, msg.toString());
        } else if (exception instanceof BussinessException) {
            BussinessException ex = (BussinessException) exception;
            return ResultInfo.error(ex.getCodeMsg(), ex.getMsg());
        } else if (exception instanceof DuplicateKeyException) {
            printExceptionInfo(request, exception);
            return ResultInfo.error(CodeMsg.DB_PRIMARKKEY_CONFLICT, "数据库主键重复");
        } else if (exception instanceof org.springframework.web.servlet.NoHandlerFoundException) {
            return ResultInfo.error(CodeMsg.SERVER_RESOURCES_UNAVAILABLE, "访问的资源不存在");
        } else if (exception instanceof CloudException) {
            return ((CloudException) exception).getRes();
        } else {
            Throwable throwable = exception;
            printExceptionInfo(request, exception);
            for (int i = 0; i < 5; i++) {
                if (throwable instanceof SQLException) {
                    if (throwable instanceof SQLTimeoutException) {
                        return ResultInfo.error(CodeMsg.DB_TIMEOUT, exception.getMessage());
                    }
                    SQLException sqlException = (SQLException) throwable;
                    String sqlState = sqlException.getSQLState();
                    if (sqlState != null) {
                        if (sqlState.equals("23505")) {
                            return ResultInfo.error(CodeMsg.DB_PRIMARKKEY_CONFLICT, exception.getMessage());
                        } else if (sqlState.startsWith("22") || sqlState.startsWith("23")) {
                            return ResultInfo.error(CodeMsg.DB_CONSTRAINT_ERROR, exception.getMessage());
                        } else if (sqlState.startsWith("08")) {
                            return ResultInfo.error(CodeMsg.DB_NETWORK_EXCPTION, exception.getMessage());
                        } else {
                            return ResultInfo.error(CodeMsg.DB_OTHER_ERROR, exception.getMessage());
                        }
                    } else {
                        return ResultInfo.error(CodeMsg.DB_OTHER_ERROR, exception.getMessage());
                    }

                }

                if (throwable != null) {
                    throwable = throwable.getCause();
                } else {
                    break;
                }
            }
            return ResultInfo.error(CodeMsg.OTHER_ERROR, exception.getMessage());
        }
    }

    private void printExceptionInfo(HttpServletRequest request, Exception exception) {
        exception.printStackTrace();
        log.error("处理【{}】发送的请求发生异常，path:{}，异常消息：{}", request.getRemoteAddr(), request.getServletPath(), exception.getMessage());
    }

}
